// Copyright 2001 FreeHEP.
package org.freehep.graphicsio.emf;

import java.awt.Point;
import java.awt.Rectangle;
import java.io.IOException;

import org.freehep.graphicsio.QuadToCubicPathConstructor;
import org.freehep.graphicsio.emf.gdi.CloseFigure;
import org.freehep.graphicsio.emf.gdi.LineTo;
import org.freehep.graphicsio.emf.gdi.MoveToEx;
import org.freehep.graphicsio.emf.gdi.PolyBezierTo;
import org.freehep.graphicsio.emf.gdi.PolyBezierTo16;
import org.freehep.graphicsio.emf.gdi.PolylineTo;
import org.freehep.graphicsio.emf.gdi.PolylineTo16;

/**
 * @author Mark Donszelmann
 * @version $Id: EMFPathConstructor.java,v 1.4 2009-08-17 21:44:45 murkle Exp $
 */
public class EMFPathConstructor extends QuadToCubicPathConstructor implements
        EMFConstants {
    private EMFOutputStream os;

    private Rectangle imageBounds;

    private boolean curved;

    private int pointIndex = 0;

    private boolean wide = false;

    private Point[] points = new Point[4];

    public EMFPathConstructor(EMFOutputStream os, Rectangle imageBounds) {
        super();
        this.os = os;
        this.imageBounds = imageBounds;
        this.curved = false;
    }

    public void move(double x, double y) throws IOException {
        flush();
        os.writeTag(new MoveToEx(new Point(toUnit(x), toUnit(y))));
        super.move(x, y);
    }

    private void addPoint(int n, double x, double y) {
        if (n >= points.length) {
            Point[] buf = new Point[n << 1];
            System.arraycopy(points, 0, buf, 0, points.length);
            points = buf;
        }

        int ix = toUnit(x);
        int iy = toUnit(y);

        if (wide || (ix < Short.MIN_VALUE) || (ix > Short.MAX_VALUE)
                || (iy < Short.MIN_VALUE) || (iy > Short.MAX_VALUE)) {
            wide = true;
        }

        if (points[n] == null) {
            points[n] = new Point(ix, iy);
        } else {
            points[n].x = ix;
            points[n].y = iy;
        }
    }

    public void line(double x, double y) throws IOException {
        if (curved && (pointIndex > 0))
            flush();
        curved = false;
        addPoint(pointIndex++, x, y);
        super.line(x, y);
    }

    public void cubic(double x1, double y1, double x2, double y2, double x3,
            double y3) throws IOException {
        if (!curved && (pointIndex > 0))
            flush();
        curved = true;
        addPoint(pointIndex++, x1, y1);
        addPoint(pointIndex++, x2, y2);
        addPoint(pointIndex++, x3, y3);
        super.cubic(x1, y1, x2, y2, x3, y3);
    }

    public void closePath(double x0, double y0) throws IOException {
        flush();
        os.writeTag(new CloseFigure());
        super.closePath(x0, y0);
    }

    public void flush() throws IOException {
        if (curved) {
            if (wide) {
                os.writeTag(new PolyBezierTo(imageBounds, pointIndex, points));
            } else {
                os
                        .writeTag(new PolyBezierTo16(imageBounds, pointIndex,
                                points));
            }
        } else if (pointIndex == 1) {
            os.writeTag(new LineTo(points[0]));
        } else if (pointIndex > 1) {
            if (wide) {
                os.writeTag(new PolylineTo(imageBounds, pointIndex, points));
            } else {
                os.writeTag(new PolylineTo16(imageBounds, pointIndex, points));
            }
        }
        pointIndex = 0;
        wide = false;
        super.flush();
    }

    protected int toUnit(double d) {
        return (int) (d * UNITS_PER_PIXEL * TWIPS);
    }
}
